Fix OpenAPI3 component response conversion to emit reusable response models - #11735
Fix OpenAPI3 component response conversion to emit reusable response models#11735Vincent Biret (baywet) with Copilot wants to merge 14 commits into
Conversation
|
Azure Pipelines: Successfully started running 1 pipeline(s). 1 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Co-authored-by: baywet <7905502+baywet@users.noreply.github.com>
|
Copilot add the changelog entry for this fix |
commit: |
Co-authored-by: baywet <7905502+baywet@users.noreply.github.com>
Co-authored-by: baywet <7905502+baywet@users.noreply.github.com>
Co-authored-by: baywet <7905502+baywet@users.noreply.github.com>
|
You can try these changes here
|
Co-authored-by: baywet <7905502+baywet@users.noreply.github.com>
| if (preferredContent && preferredContent[0] !== "application/json") { | ||
| properties.push({ | ||
| name: "contentType", | ||
| decorators: [{ name: "header", args: [] }], | ||
| isOptional: false, | ||
| schema: { type: "string", enum: [preferredContent[0]] }, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Addressed in 0b5c035 by emitting the synthetic content type property as @header("Content-Type") contentType and updating the regression assertion.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
All changed packages have been documented.
Show changes
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
packages/openapi3/src/cli/actions/convert/transforms/transforms.ts:321
responsesis assigned but never used. WithnoUnusedLocals/linting enabled this will fail the build; even without it, this local can be removed without changing behavior.
const responses = context.openApi3Doc.components?.responses;
if (!responses) return;
Co-authored-by: baywet <7905502+baywet@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
packages/openapi3/src/cli/actions/convert/transforms/transforms.ts:321
transformComponentResponsesdeclaresresponsesbut never uses it. This adds dead code and makes it harder to see what the function actually depends on.
You can inline the existence check against context.openApi3Doc.components?.responses and remove the unused local.
const responses = context.openApi3Doc.components?.responses;
if (!responses) return;
packages/openapi3/src/cli/actions/convert/transforms/transforms.ts:389
getResponsePropertiestakesstatusCode: string, but then passes it toconvertStatusCodeToProperty, which expectsExclude<StatusCodes, "default">. This is a TypeScript type error (a plainstringisn’t assignable toStatusCodes) and can break the build.
Consider typing getResponseProperties to StatusCodes (via a type query) and casting the iterated statusCode when calling it, since Object.entries(...) loses the literal key type.
function getResponseProperties(
statusCode: string,
response: OpenAPI3Response,
context: Context,
): TypeSpecModelProperty[] {
Signed-off-by: Vincent Biret <vibiret@microsoft.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
packages/openapi3/src/cli/actions/convert/generators/generate-response-expressions.ts:48
- The early return for
#/components/responses/...refs bypasses the existing OpenAPI 3.2 SSE (text/event-stream+itemSchema) handling later ingenerateResponseExpressions, so SSE component responses would incorrectly return the generated response model instead ofSSEStream<...>.
if ("$ref" in props.response && props.response.$ref.startsWith("#/components/responses/")) {
const componentResponseName = context.getComponentResponseName(props.response.$ref, statusCode);
if (componentResponseName) {
return [componentResponseName];
}
packages/openapi3/test/tsp-openapi3/convert-openapi3-doc.test.ts:285
- This assertion hard-codes an exact newline + indentation sequence (
"@error\n model ..."), which is brittle across formatting changes. Prefer a whitespace-tolerant regex so the test only cares that@erroris applied to the model.
strictEqual(
tsp.includes("@error\n model RejectedDefault"),
true,
"Expected default response model to be marked as an error: " + tsp,
);
The OpenAPI3 import path was flattening referenced component responses into ad hoc inline response objects, which duplicated response metadata across operations and prevented reusable response models. This made shared component responses harder to maintain and could produce awkward TypeSpec definitions when the same response was referenced repeatedly.
Summary
#/components/responses/...references instead of inlining the response body at each operation.Responsesand preserves the status code, headers, and body schema from the component response.What changed
application/jsonpayload when multiple content types exist.Example
This keeps component responses reusable and reduces duplication while preserving the semantics of the original OpenAPI description.